In the early years of computer science, computer code was developed to run directly on a target machines in “native” format. This format for code storage and distribution remains popular today. However, a relatively newer model for code execution manages the code rather than allowing direct execution. Typically such code is stored and transported in an “intermediate language” form that is not itself executable. This increases the ease with which the code can be distributed over network connections to various machines of different architectures to run on each, since the intermediate language code can be compiled into native code to suit each target machine just before or even during runtime. Thus, for example, a piece of intermediate language code may be distributed in the same form to both an X86 and an IA64 machine and can run on both machines once compiled.
The reason that the portable representation is called “intermediate language” is that it is still in fact a refinement of higher level code, even though it has not been compiled to native code yet. Generally, a developer or group of developers writes the source code for a program or executable. Once the source code has been completed, it is compiled into the intermediate representation. The intermediate code can then be distributed and further compiled as described above.
Typically, the compilation and execution of the IL code is controlled or managed on the target machine by a runtime environment, e.g. another module or entity other than the code itself or the operating system of the target machine. For this reason, this type of code is generally referred to as managed code, whereas code that runs directly in native format without management is generally referred to as unmanaged code. One example of managed code is code produced to operate with the Common Language Runtime (CLR) designed by Microsoft Corporation of Redmond, Wash. Examples of typically unmanaged code include VB6, native C++, etc.
Managed code may be, but need not always be, slower in execution, however there are a number of benefits other than ease of distribution to be gained with the use of managed code. For example, a runtime environment can provide memory management services such as garbage collection, and may also provide security functions, reflection, etc.
With respect to all types of code, managed and unmanaged alike, it is desirable that the code be as free of errors as possible to assure a high quality product for the end user. Typically this requires that the code be error tested, or “debugged,” prior to distribution. Unfortunately, managed code has proven to be more difficult to test for errors or “bugs” than unmanaged code due to the fact that it runs within a runtime environment.
One current solution is to place a debugger module into the code itself so that the debugger runs when the code runs, both being run within the run-time environment (in-process). Information regarding the state of the code is gleaned by the debugger from the memory used by the code via a COM pipeline or otherwise. However, this solution is not entirely satisfactory for a number of reasons. First, by having another entity, the debugger, running with the code under test, the actual operation of the code under test may be perturbed so that the test does not accurately reflect realistic operating conditions for the code. Moreover, this type of architecture typically requires that the debugger be stopped whenever execution of the code is stopped. This is undesirable in that it may be useful to check the state of the code while it is halted, or to check the state of the code based on a dump file after a crash.